package glassline;

import java.util.List;
import java.util.ArrayList;

import transducer.TChannel;
import transducer.TEvent;
import transducer.TReceiver;
import transducer.Transducer;

import glassline.ConveyorAgent.MySensor;
import glassline.interfaces.Conveyor;
import glassline.interfaces.Popup;
import glassline.interfaces.Workstation;
import agent.Agent;

public class PopupAgent extends Agent implements Popup, TReceiver {

	enum WorkstationStatus {isFull, isEmpty};
	class MyWorkstation{
		public WorkstationStatus status;
		Workstation work;
		
		public MyWorkstation(Workstation w, WorkstationStatus s){
			status = s;
			work = w;
		}
	}
	
	//list of workstations associated with the popup
	public List<MyWorkstation> workstations = new ArrayList<MyWorkstation>();
	
	//ID to be used with args in the transducer
	Object[] myID = new Object[1];
	
	public boolean on = true;
	
	public enum PopupStatus {COME_UP, COME_DOWN, WAITING_FOR_UP, WAITING_FOR_DOWN, LOCK_DOWN, FROM_WORKSTATION, FROM_CONVEYOR, UP_EMPTY, UP_FULL, DOWN_EMPTY, DOWN_FULL, UP_REQUESTED};
	public enum GlassStatus {onPopup, inWorkstation};
	
	public class MyGlass{
		public Glass glass;
		MySensor location;
		public GlassStatus state;
		
		
		public MyGlass(Glass g,  GlassStatus s){
			glass = g;
			//location = m;
			state = s;
		}
	}
	
	public MyGlass theGlass;
	Transducer transducer;
	Conveyor conv;
	public PopupStatus status;
	
	//list of workstations requesting popup to come up
	List<Workstation> requestingWorkstations = new ArrayList<Workstation>();
	
	boolean canRaise = true;
	
	//constructor
	public PopupAgent(Transducer t, Conveyor c, int i){
		transducer = t;
		conv = c;
		status = PopupStatus.DOWN_EMPTY;
		transducer.register(this, TChannel.POPUP);
		myID[0] = i;
	}
	
	//assigns a workstation to this PopupAgent
	public void addWorkstation(Workstation w){
		MyWorkstation work = new MyWorkstation(w, WorkstationStatus.isEmpty);
		workstations.add(work);
	}
	
	//Scheduler
	@Override
	public boolean pickAndExecuteAnAction() {
		if(on && canRaise){
			
			//if popup is up and empty, just go down
			if(status == PopupStatus.UP_EMPTY){
				goDown();
				return true;
			}
			
			//if popup is up and full,
			//if the glass in the popup needs to go to either of the workstations,
			//give glass to correct workstation
			//otherwise, go down
			else if(status == PopupStatus.UP_FULL){
				for(MyWorkstation w: workstations){
					if(theGlass.glass.recipe.workstations.containsKey(w.work.getType())){
						if(theGlass.glass.recipe.workstations.get(w.work.getType()) == false){
							giveGlassToWorkstation(w);
							return true;
						}
					}
					
					goDown();
					return true;
				}				
			}
			
			//if popup is down and full,
			//if the glass in the popup needs to go to either of the workstations,
			//go up so that they can be given
			//otherwise, give glass back to conveyor
			else if(status == PopupStatus.DOWN_FULL){
				for(MyWorkstation w: workstations){
					if(theGlass.glass.recipe.workstations.containsKey(w.work.getType())){
						if(theGlass.glass.recipe.workstations.get(w.work.getType()) == false){
							goUp(null);
							return true;
						}
					}
					
					giveGlassToConveyor();
					return true;
				}			
			}
			
			//if popup animation going down has finished,
			//tell conveyor
			else if(status == PopupStatus.COME_DOWN){
				tellConveyorIAmDown();
				return true;
			}
			
			//if popup animation going up has finished,
			//tell workstation
			else if(status == PopupStatus.COME_UP){
				tellWorkstationIAmUp();
			}
			
			
		}
		
		return false;
	}

	//Messages
	
	//glass recieved from a conveyor
	@Override
	public void msgHereIsGlass(Glass glass, Conveyor conveyor) {
		Do("recieved msgHereIsGlass from a conveyor");
		theGlass = new MyGlass(glass, GlassStatus.onPopup);
		status = PopupStatus.DOWN_FULL; //implies PopupStatus.DOWN_FULL
		stateChanged();
	}
	
	//glass recieved from a workstation
	@Override
	public void msgHereIsGlass(Glass glass, Workstation workstation) {
		Do("recieved msgHereIsGlass from a workstation");
		theGlass = new MyGlass(glass, GlassStatus.onPopup); //implies PopupStatus.UP_FULL
		status = PopupStatus.UP_FULL;
		
		for(MyWorkstation w: workstations){
			if(w.work == workstation){
				w.status = WorkstationStatus.isEmpty;
			}
		}
		
		stateChanged();
	}

	//locks popup down when glass is going over it
	public void msgDoNotRaise(){
		Do("recieved msgDoNotRaise");
		canRaise = false;
		status = PopupStatus.LOCK_DOWN;
		stateChanged();
	}
	
	//unlocks popup
	public void msgYouCanRaise(){
		Do("recieved msgYouCanRaise");
		canRaise = true;
		status = PopupStatus.DOWN_EMPTY;
		stateChanged();
	}
	
	//message telling popup to go up
	public void msgComeUp(Workstation w){
		Do("recieved msgComeUp");
		goUp(w);
	}
	
	
	//Actions
	//gives glass to workstation w and sets it to full
	public void giveGlassToWorkstation(MyWorkstation w){
		Do("Giving glass to workstation " + w);
		status = PopupStatus.UP_EMPTY;
		w.status = WorkstationStatus.isFull;
		w.work.msgHereIsGlass(theGlass.glass);
		theGlass = null;
		stateChanged();
		
	}
	
	//gives glass to conveyor and sets popup to down and empty
	public void giveGlassToConveyor(){
		Do("Giving glass to conveyor " + conv);
		status = PopupStatus.DOWN_EMPTY;
		conv.msgHereIsGlass(theGlass.glass, this);
		theGlass = null;
		stateChanged();
	}
	
	//sends proper msgIAmUp to conveyor based on whether or not the workstations or full
	//also adds workstation requesting it to go up (if it exists) to proper list
	public void goUp(Workstation w){
		Do("Popup going up");
		transducer.fireEvent(TChannel.POPUP, TEvent.POPUP_GO_UP, myID);
		status = PopupStatus.WAITING_FOR_UP;
		
		int count = 0;
		for(MyWorkstation ws: workstations){	
			if(ws.status == WorkstationStatus.isEmpty){
				count++;
			}
		}
		
		if(w == null){
			if(count == 1){
				conv.msgIAmUp(true, this);
			}
			
			else{
				conv.msgIAmUp(this);	
			}
		}
		
		
		//TO DO -> fix this so that workstation is messaged *AFTER* the animation finishes
		//i.e. after event is fired
		if(w != null)
			requestingWorkstations.add(w);
		stateChanged();
	}
	
	//goes down
	public void goDown(){
		Do("Popup going down");
		transducer.fireEvent(TChannel.POPUP, TEvent.POPUP_GO_DOWN, myID);
		status = PopupStatus.WAITING_FOR_DOWN;
		stateChanged();
	}
	
	//messages conveyor popup is down
	public void tellConveyorIAmDown(){
		status = PopupStatus.DOWN_EMPTY;
		conv.msgIAmDown(this);
		stateChanged();
	}
	
	//messages workstation popup is up
	public void tellWorkstationIAmUp(){
		status = PopupStatus.UP_EMPTY;
		requestingWorkstations.remove(0).msgIAmUp();
		stateChanged();
	}
	
	//eventFired method for transducer
	@Override
	public void eventFired(TChannel channel, TEvent event, Object[] args) {
		if(channel == TChannel.POPUP)
		{
				if(event == TEvent.POPUP_NOW_DOWN)
				{
					if(args[0] == myID[0]){
					if(theGlass == null)
						status = PopupStatus.COME_DOWN;
					else
						status = PopupStatus.DOWN_FULL;
					stateChanged();
					}
				}
				
				else if(event == TEvent.POPUP_NOW_UP){
					if(args[0] == myID[0]){
					if(theGlass == null)
						status = PopupStatus.COME_UP;
					else
						status = PopupStatus.UP_FULL;
					stateChanged();
					}
				}
			}
		}
	
	//toString method for testing purposes
	public String toString()
	{
		return ("Popup " + myID[0]);
	}
	
}
	

